#include "logger.hpp"

Logger::Logger() {
  init();
  hasGUI=false;
}

Logger::Logger(QWidget *parent) : buffer() {
  init();
  hasGUI=true;
  logWindow=new QDialog(parent);
  logWindow->setWindowTitle(QString("Log"));
  logWindow->setModal(false);
  logWindow->setSizeGripEnabled(true);
  QVBoxLayout *mainLayout=new QVBoxLayout();
  QHBoxLayout *buttonLayout=new QHBoxLayout();
  QPushButton *clearButton=new QPushButton(QString("Clear"));
  connect(clearButton,SIGNAL(pressed()),this,SLOT(clear()));
  QPushButton *saveButton=new QPushButton(QString("Save"));
  saveButton->setDefault(true);
  connect(saveButton,SIGNAL(pressed()),this,SLOT(save()));
  listView=new QListView();
  listView->setEditTriggers(QAbstractItemView::NoEditTriggers);
  logWindow->resize(600,400);
  QScrollBar *scrollbar=listView->verticalScrollBar();
  connect(scrollbar,SIGNAL(rangeChanged(int,int)),this,SLOT(scrollToBottom(int,int)));

  listModel=new QStringListModel(*messages,this);
  filterModel=new QSortFilterProxyModel(this);
  filterModel->setSourceModel(listModel);
  listView->setModel(filterModel);
  filterLabel=new QLabel(tr("Filter:"));
  filterInput=new QLineEdit();
  connect(filterInput,SIGNAL(textChanged(QString)),this,SLOT(filter(QString)));
  
  buttonLayout->addWidget(filterLabel);
  buttonLayout->addWidget(filterInput);
  buttonLayout->addStretch();
  buttonLayout->addWidget(clearButton);
  buttonLayout->addWidget(saveButton);
  mainLayout->addWidget(listView);
  mainLayout->addLayout(buttonLayout);
  logWindow->setLayout(mainLayout);
}

void Logger::init() {
  enabled=true;
  logWindow=NULL;
  //messages=NULL;
  listModel=NULL;
  listView=NULL;
  filterInput=NULL;
  messages=new QStringList();
  buffer=new QStringList();
}

Logger::~Logger() {
  /*
  if (logWindow!=NULL) {
    delete logWindow;
    logWindow=NULL;
  }
  if (listView!=NULL) {
    delete listView;
    listView=NULL;
  }
  if (listModel!=NULL) {
    delete listModel;
    listModel=NULL;
  }
  if (messages!=NULL) {
    delete messages;
    messages=NULL;
  }
  */
}

void Logger::setEnabled(bool enabled) {
  buffer->clear();
  this->enabled=enabled;
}

void Logger::show() {
  if (hasGUI) {
    QScrollBar *scrollBar=listView->verticalScrollBar();
    scrollBar->setValue(scrollBar->maximum());
    if (logWindow!=NULL) logWindow->show();
  } else {
    QTextStream out(stdout);
    QStringListIterator i(*messages);
    while (i.hasNext()) {
      out << i.next() << endl;
    }
  }
}

void Logger::filter(const QString &pattern) {
  filterModel->setFilterFixedString(pattern);
}

void Logger::save() {
  if (hasGUI) {
    QString fileName=QFileDialog::getSaveFileName(logWindow,tr("Save Log"),QDir::homePath(),tr("Log files (*.log)"));
    save(fileName);
  }
}

void Logger::save(const char *fileName) {
  save(QString(fileName));
}

void Logger::save(const QString &fileName) {
  QFile file(fileName);
  if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    return;
  QTextStream out(&file);
  QStringListIterator i(*messages);
  while (i.hasNext()) {
    out << i.next() << endl;
  }
  file.close();
}

void Logger::clear() {
  if (filterInput) filterInput->clear();
  buffer->clear();
  messages->clear();
  if (listModel) listModel->setStringList(*messages);
}

void Logger::append(const char *message) {
  if (enabled) append(QString(message).trimmed());
}

void Logger::append(const QString &message) {
  if (enabled) {
    QString now=QDateTime::currentDateTime().toString(QString("yyyy.MM.dd-hh.mm.ss"));
    messages->append(QString("%1: %2").arg(now).arg(message.trimmed()));
    if (listModel) listModel->setStringList(*messages);
  }
}

void Logger::scrollToBottom(int min,int max) {
  Q_UNUSED(min);
  listView->verticalScrollBar()->setValue(max);
}

void Logger::flush() {
  if (enabled) {
    append(buffer->join(QString(" ")));
    buffer->clear();
  }
}

Logger & Logger::operator<<(double f) {
  if (enabled) buffer->append(QString("%1").arg(f));
  return *this;
}

Logger & Logger::operator<<(signed short i) {
  if (enabled) buffer->append(QString("%1").arg(i));
  return *this;
}

Logger & Logger::operator<<(float f) {
  if (enabled) buffer->append(QString("%1").arg(f));
  return *this;
}

Logger & Logger::operator<<(const QString & string) {
  if (enabled) buffer->append(string.trimmed());
  return *this;
}

Logger & Logger::operator<<(char c) {
  if (enabled) buffer->append(QString("%1").arg(c));
  return *this;
}

Logger & Logger::operator<<(unsigned short i) {
  if (enabled) buffer->append(QString("%1").arg(i));
  return *this;
}

Logger & Logger::operator<<(signed int i) {
  if (enabled) buffer->append(QString("%1").arg(i));
  return *this;
}

Logger & Logger::operator<<(unsigned int i) {
  if (enabled) buffer->append(QString("%1").arg(i));
  return *this;
}

Logger & Logger::operator<<(signed long i) {
  if (enabled) buffer->append(QString("%1").arg(i));
  return *this;
}

Logger & Logger::operator<<(unsigned long i) {
  if (enabled) buffer->append(QString("%1").arg(i));
  return *this;
}

Logger & Logger::operator<<(qlonglong i) {
  if (enabled) buffer->append(QString("%1").arg(i));
  return *this;
}

Logger & Logger::operator<<(qulonglong i) {
  if (enabled) buffer->append(QString("%1").arg(i));
  return *this;
}

Logger & Logger::operator<<(const QByteArray & array) {
  if (enabled) buffer->append(QString(array).trimmed());
  return *this;
}

Logger & Logger::operator<<(const char * string) {
  if (enabled) buffer->append(QString(string).trimmed());
  return *this;
}

Logger & Logger::operator<<(const Vector3d &vector) {
  if (enabled) buffer->append(QString("%1 %2 %3").arg(vector.x()).arg(vector.y()).arg(vector.z()));
  return *this;
}

Logger & Logger::operator<<(Logger & (*func)(Logger &)) {
  return (*func)(*this);
}

Logger & endl(Logger &logger) {
  logger.flush();
  return logger;
}

/*
Logger & Logger::appendTableRow(QStringList &columns,QList<int> &columnWidths) {
  return *this;
}
*/

Logger & Logger::appendTableRow(QStringList &columns,int columnWidth) {
  //make sure there are no messages in the buffer
  if (enabled) {
    if (buffer->size()>0) flush();
    QStringListIterator i(columns);
    while (i.hasNext()) {
      buffer->append(QString("%1").arg(i.next(),-columnWidth));
    }
    flush();
  }
  return *this;
}
